{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "编辑graph状态\n",
    "![](../images/edit_graph_state.png)\n",
    "\n",
    "## Simple Usage"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f8b76c56ee7a1c63"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "<langgraph.graph.state.StateGraph at 0x10873fb90>"
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing_extensions import TypedDict\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "from IPython.display import Image, display\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    input: str\n",
    "\n",
    "\n",
    "def step_1(state):\n",
    "    print(\"---Step 1---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_2(state):\n",
    "    print(\"---Step 2---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "def step_3(state):\n",
    "    print(\"---Step 3---\")\n",
    "    pass\n",
    "\n",
    "\n",
    "workflow = StateGraph(State)\n",
    "workflow.add_node(\"step_1\", step_1)\n",
    "workflow.add_node(\"step_2\", step_2)\n",
    "workflow.add_node(\"step_3\", step_3)\n",
    "\n",
    "workflow.add_edge(START, \"step_1\")\n",
    "workflow.add_edge(\"step_1\", \"step_2\")\n",
    "workflow.add_edge(\"step_2\", \"step_3\")\n",
    "workflow.add_edge(\"step_3\", END)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:26:42.615420Z",
     "start_time": "2024-11-05T01:26:41.162763Z"
    }
   },
   "id": "7757e6f789c1c7c",
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set up memory\n",
    "memory = MemorySaver()\n",
    "\n",
    "# Add\n",
    "graph = workflow.compile(checkpointer=memory, interrupt_before=[\"step_2\"])\n",
    "\n",
    "# View\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:26:53.125842Z",
     "start_time": "2024-11-05T01:26:51.401561Z"
    }
   },
   "id": "83c77b72239d7dc3",
   "execution_count": 2
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello world'}\n",
      "---Step 1---\n"
     ]
    }
   ],
   "source": [
    "# Input\n",
    "initial_input = {\"input\": \"hello world\"}\n",
    "\n",
    "# Thread\n",
    "thread = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "# Run the graph until the first interruption\n",
    "for event in graph.stream(initial_input, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:27:41.931597Z",
     "start_time": "2024-11-05T01:27:41.908969Z"
    }
   },
   "id": "1cc30fc54eaf0dfc",
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "source": [
    "通过`update_state`函数，我们可以通过thread(RunnableConfig)为条件更新状态。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "ac5a9084d135d030"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Current state!\n",
      "{'input': 'hello world'}\n",
      "---\n",
      "---\n",
      "Updated state!\n",
      "{'input': 'hello universe!'}\n"
     ]
    }
   ],
   "source": [
    "print(\"Current state!\")\n",
    "print(graph.get_state(thread).values)\n",
    "\n",
    "graph.update_state(thread, {\"input\": \"hello universe!\"})\n",
    "\n",
    "print(\"---\\n---\\nUpdated state!\")\n",
    "print(graph.get_state(thread).values)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:28:35.354623Z",
     "start_time": "2024-11-05T01:28:35.344565Z"
    }
   },
   "id": "92a4e3f2c27beae7",
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'input': 'hello universe!'}\n",
      "---Step 2---\n",
      "---Step 3---\n"
     ]
    }
   ],
   "source": [
    "# Continue the graph execution\n",
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    print(event)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:31:04.142888Z",
     "start_time": "2024-11-05T01:31:04.130505Z"
    }
   },
   "id": "e61ece572bfd7f85",
   "execution_count": 5
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Agent\n",
    "在Agent的上下文中，更新状态对于编辑工具调用等事情很有用。\n",
    "为了说明这一点，我们将构建一个相对简单的 ReAct 风格Agent来调用工具。\n",
    "我们将使用 ChatOpenAI 的模型和假工具（仅用于演示目的）。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "eaf3f173836f7ca6"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from dotenv import load_dotenv\n",
    "import os\n",
    "from langchain_openai import ChatOpenAI\n",
    "# Set up the tool\n",
    "from langchain_core.tools import tool\n",
    "from langgraph.graph import MessagesState, START, END, StateGraph\n",
    "from langgraph.prebuilt import ToolNode\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "\n",
    "@tool\n",
    "def search_web(query: str):\n",
    "    \"\"\"Call to surf the web.\"\"\"\n",
    "    # This is a placeholder for the actual implementation\n",
    "    # Don't let the LLM know this though 😊\n",
    "    return [\n",
    "        \"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"\n",
    "    ]\n",
    "\n",
    "\n",
    "tools = [search_web]\n",
    "tool_node = ToolNode(tools)\n",
    "\n",
    "\n",
    "# Define nodes and conditional edges\n",
    "\n",
    "\n",
    "# Define the function that determines whether to continue or not\n",
    "def should_continue(state):\n",
    "    messages = state[\"messages\"]\n",
    "    last_message = messages[-1]\n",
    "    # If there is no function call, then we finish\n",
    "    if not last_message.tool_calls:\n",
    "        return \"end\"\n",
    "    # Otherwise if there is, we continue\n",
    "    else:\n",
    "        return \"continue\"\n",
    "\n",
    "\n",
    "# Define the function that calls the model\n",
    "def call_model(state):\n",
    "    messages = state[\"messages\"]\n",
    "    response = model.invoke(messages)\n",
    "    # We return a list, because this will get added to the existing list\n",
    "    return {\"messages\": [response]}\n",
    "\n",
    "\n",
    "load_dotenv()\n",
    "model = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0, streaming=True,\n",
    ").bind_tools(tools)\n",
    "\n",
    "workflow = StateGraph(MessagesState)\n",
    "workflow.add_node(\"agent\", call_model)\n",
    "workflow.add_node(\"action\", tool_node)\n",
    "\n",
    "workflow.add_edge(START, \"agent\")\n",
    "workflow.add_conditional_edges(\"agent\", should_continue,\n",
    "                               # Finally we pass in a mapping.\n",
    "                               # The keys are strings, and the values are other nodes.\n",
    "                               # END is a special node marking that the graph should finish.\n",
    "                               # What will happen is we will call `should_continue`, and then the output of that\n",
    "                               # will be matched against the keys in this mapping.\n",
    "                               # Based on which one it matches, that node will then be called.\n",
    "                               {\n",
    "                                   # If `tools`, then we call the tool node.\n",
    "                                   \"continue\": \"action\",\n",
    "                                   # Otherwise we finish.\n",
    "                                   \"end\": END,\n",
    "                               },\n",
    "                               )\n",
    "# We now add a normal edge from `tools` to `agent`.\n",
    "# This means that after `tools` is called, `agent` node is called next.\n",
    "workflow.add_edge(\"action\", \"agent\")\n",
    "# Set up memory\n",
    "memory = MemorySaver()\n",
    "\n",
    "# Finally, we compile it!\n",
    "# This compiles it into a LangChain Runnable,\n",
    "# meaning you can use it as you would any other runnable\n",
    "\n",
    "# We add in `interrupt_before=[\"action\"]`\n",
    "# This will add a breakpoint before the `action` node is called\n",
    "graph = workflow.compile(checkpointer=memory, interrupt_before=[\"action\"])\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:43:33.495596Z",
     "start_time": "2024-11-05T01:43:32.102787Z"
    }
   },
   "id": "4e5f5c616a3dcf2b",
   "execution_count": 9
  },
  {
   "cell_type": "markdown",
   "source": [
    "## 与Agent交互\n",
    "现在我们可以与Agent交互，并看到它在调用工具之前会停下来。\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "cb2a1398381d69ec"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001B[1m Human Message \u001B[0m=================================\n",
      "\n",
      "search for the weather in sf now\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  search_web (call_c47e48e6761e491eb06ee9)\n",
      " Call ID: call_c47e48e6761e491eb06ee9\n",
      "  Args:\n",
      "    query: current weather in San Francisco\n"
     ]
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "thread = {\"configurable\": {\"thread_id\": \"3\"}}\n",
    "inputs = [HumanMessage(content=\"search for the weather in sf now\")]\n",
    "for event in graph.stream({\"messages\": inputs}, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:45:55.620602Z",
     "start_time": "2024-11-05T01:45:54.090554Z"
    }
   },
   "id": "32dbcb162fbd6235",
   "execution_count": 10
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Edit\n",
    "现在我们可以相应地更新状态。让我们修改工具调用以获取查询\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "ba5b894d51ef3fbc"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "last message: content='' additional_kwargs={'tool_calls': [{'index': 0, 'id': 'call_c47e48e6761e491eb06ee9', 'function': {'arguments': '{\"query\": \"current weather in San Francisco\"}', 'name': 'search_web'}, 'type': 'function'}]} response_metadata={'finish_reason': 'tool_calls', 'model_name': 'qwen-max'} id='run-abdcc715-8364-4c4d-98cd-fde9bb22d5f7-0' tool_calls=[{'name': 'search_web', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_c47e48e6761e491eb06ee9', 'type': 'tool_call'}]\n"
     ]
    },
    {
     "data": {
      "text/plain": "{'configurable': {'thread_id': '3',\n  'checkpoint_ns': '',\n  'checkpoint_id': '1ef9b184-ab84-604a-8002-95604869d373'}}"
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "current_state = graph.get_state(thread)\n",
    "# Let's now get the last message in the state\n",
    "# This is the one with the tool calls that we want to update\n",
    "last_message = current_state.values[\"messages\"][-1]\n",
    "print(f\"last message: {last_message}\")\n",
    "# Let's now update the args for that tool call\n",
    "last_message.tool_calls[0][\"args\"] = {\"query\": \"current weather in SF\"}\n",
    "# Let's now call `update_state` to pass in this message in the `messages` key\n",
    "# This will get treated as any other update to the state\n",
    "# It will get passed to the reducer function for the `messages` key\n",
    "# That reducer function will use the ID of the message to update it\n",
    "# It's important that it has the right ID! Otherwise it would get appended\n",
    "# as a new message\n",
    "graph.update_state(thread, {\"messages\": last_message})"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:50:10.694531Z",
     "start_time": "2024-11-05T01:50:10.664756Z"
    }
   },
   "id": "cf57545186710ae4",
   "execution_count": 11
  },
  {
   "cell_type": "markdown",
   "source": [
    "现在，让我们检查应用程序的当前状态，以确保其已相应更新\n",
    "\n"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "9743bae6761ec9b8"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "text/plain": "[{'name': 'search_web',\n  'args': {'query': 'current weather in SF'},\n  'id': 'call_c47e48e6761e491eb06ee9',\n  'type': 'tool_call'}]"
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "current_state = graph.get_state(thread).values[\"messages\"][-1].tool_calls\n",
    "current_state"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:51:24.003922Z",
     "start_time": "2024-11-05T01:51:23.994088Z"
    }
   },
   "id": "edc69ed3ed3ee6dc",
   "execution_count": 12
  },
  {
   "cell_type": "markdown",
   "source": [
    "恢复\n",
    "\n",
    "我们现在可以再次调用代理而不输入任何内容以继续，即按要求运行工具。从日志中我们可以看到，它向工具传递了更新参数。"
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "e84fa27b3a4ea32a"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  search_web (call_c47e48e6761e491eb06ee9)\n",
      " Call ID: call_c47e48e6761e491eb06ee9\n",
      "  Args:\n",
      "    query: current weather in SF\n",
      "=================================\u001B[1m Tool Message \u001B[0m=================================\n",
      "Name: search_web\n",
      "\n",
      "[\"It's sunny in San Francisco, but you better look out if you're a Gemini 😈.\"]\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "\n",
      "The current weather in San Francisco is sunny. However, the source included a humorous note saying \"but you better look out if you're a Gemini 😈.\" This seems like a playful addition and not related to the weather, so please enjoy the sun!\n"
     ]
    }
   ],
   "source": [
    "for event in graph.stream(None, thread, stream_mode=\"values\"):\n",
    "    event[\"messages\"][-1].pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T01:52:34.818502Z",
     "start_time": "2024-11-05T01:52:31.384144Z"
    }
   },
   "id": "5208ada20d09a51d",
   "execution_count": 13
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
